#include "wpgraphicslist.h"

#include <QGraphicsLinearLayout>
#include <QGraphicsView>
#include <QGraphicsSceneResizeEvent>
#include <QPropertyAnimation>

#include "wpgraphicslistitem.h"
#include "wpscrollarea.h"
#include "PopupMenu.h"
#include "themedatastorage.h"
#include "enume/pannableview.h"
#include "BlogView.h"
#include "wpcommongeometry.h"
#include "utils.h"

WPGraphicsList::WPGraphicsList(BlogView* aBlogView, const QGraphicsView& aGraphicsView,
	        PannableView* aPannableView)
  : focusFunctor(*this)
  , iCurrentItem(0, focusFunctor)
  , iBusyIndicator(aBlogView->GetBusyIndicator())
  , iThemeData(aBlogView->GetThemeData())
  , iRepositionAfterMenuHide(false)
  , iGraphicsView(aGraphicsView) 
  , iPopupMenu(new PopupMenu)
#ifndef Q_WS_MAEMO_5
  , iAnimation(NULL)
  , iMenuHiding(false)
#endif /* Q_WS_MAEMO_5 */
  , iPannableView(aPannableView)
{
	iLayout = new QGraphicsLinearLayout(Qt::Vertical);
	iLayout->setSpacing(0);
	iLayout->setContentsMargins(0, 0, 0, 0);
    setContentsMargins(0, 0, 0, 0);
    //iLayout->addStretch();
    setLayout(iLayout);
    
    iPopupMenu->setWindowOpacity(0.6);
    connect(iPopupMenu, SIGNAL(aboutToHide()), this, SLOT(MenuHiding()));
}

WPGraphicsList::~WPGraphicsList()
{
    delete iPopupMenu;
}

QGraphicsLinearLayout * WPGraphicsList::AddLayout()
{
    QGraphicsLinearLayout * childLayout = new QGraphicsLinearLayout(Qt::Vertical);
    childLayout->setSpacing(0);
    childLayout->setContentsMargins(0, 0, 0, 0);
    iLayout->addItem(childLayout);
    childLayout->setParentLayoutItem(iLayout);
    return childLayout;
}

void WPGraphicsList::ClearLayout(QGraphicsLinearLayout & layout)
{
    while(layout.count()) {
        QGraphicsItem * item = layout.itemAt(0)->graphicsItem();
        if(iCurrentItem == item) { iCurrentItem = 0; }
        layout.removeAt(0);
        delete item;
    }
}

void WPGraphicsList::ShowContextMenu(WPGraphicsListItem* aItem)
{
    iCurrentItem = aItem;
    WPGraphicsListItem * listItem = CurrentListItem();
    if(!listItem) { return; }
    
    if (iPopupMenu->isVisible())
        return;

    if (aItem->IsBusy() || aItem->BogusFocus())
        return;

    iCurrentItem = aItem;
    listItem->MarkBogusFocus(true);
    
    iPopupMenu->clear();
    PopulatePopupMenuForCurrentItem(iPopupMenu);
    iPopupMenu->show();

    //QPoint itemPoint = iGraphicsView.mapFromScene(aItem->TopRight());
    //itemPoint = getMenuLocation(itemPoint);
    QPoint viewPoint = iGraphicsView.mapToGlobal(iGraphicsView.mapFromScene(aItem->scenePos()));
    
    int x = viewPoint.x() + iGraphicsView.viewport()->width();
    int y = viewPoint.y();
    
    // fix out of screen
    //
    if (y + iPopupMenu->height() > iGraphicsView.viewport()->height())
    {
        y -= y + iPopupMenu->height() - iGraphicsView.viewport()->height();
    }
    if (y < 0) { y = 0; }

    // destination point to place popup menu at
    QRect dest(x - iPopupMenu->width(), y, iPopupMenu->width(), iPopupMenu->height());
    
    if(!iThemeData.AnimateMenus())
    {
        iPopupMenu->setGeometry(dest);
        return;
    }
    
    if (iAnimation)
    {
        delete iAnimation;
        iAnimation = NULL;
    }

    iAnimation = new QPropertyAnimation(iPopupMenu, "geometry", this);
    iAnimation->setDuration(200);

    qDebug("WP::Animating iPopupMenu FROM %d, %d TO %d, %d", x, y, x - iPopupMenu->width(), y);

    iAnimation->setStartValue(dest.adjusted(iPopupMenu->width(), 0, iPopupMenu->width(), 0));
    iAnimation->setEndValue(dest);
    iAnimation->setEasingCurve(QEasingCurve::Linear);
    iAnimation->start();
}

void WPGraphicsList::ShowContextMenuForCurrent()
{
    WPGraphicsListItem * listItem = CurrentListItem();
    if(listItem)
        ShowContextMenu(listItem);
}

void WPGraphicsList::ContextMenuHidden()
{
    if(iThemeData.AnimateMenus()) {
        iPopupMenu->hide();
        iMenuHiding = false;
        delete iAnimation;
        iAnimation = NULL;
    }
    
    setFocus(iRepositionAfterMenuHide);
    iRepositionAfterMenuHide = false;
}

void WPGraphicsList::MenuHiding()
{
    if(!iThemeData.AnimateMenus()) {
        // direct
        ContextMenuHidden();
        return;
    }
 
    // never take this event twice.
    qDebug("WP::WPGraphicsList::MenuHiding()");
    if (iMenuHiding) return;
    
    if (iAnimation)
    {
        delete iAnimation;
        iAnimation = NULL;
    }
    
    iPopupMenu->show();

    iAnimation = new QPropertyAnimation(iPopupMenu, "geometry", this);
    iAnimation->setDuration(200);
    
    QRectF itemGeometry = iPopupMenu->geometry();
    
    int x = int(itemGeometry.x());
    int y = int(itemGeometry.y());

    qDebug("WP::Animating iPopupMenu FROM %d, %d TO %d, %d", x, y, x + iPopupMenu->width(), y);

    iAnimation->setStartValue(QRect(x,y,iPopupMenu->width(),iPopupMenu->height()));
    iAnimation->setEndValue(QRect(x+iPopupMenu->width(),y,iPopupMenu->width(),iPopupMenu->height()));
    iAnimation->setEasingCurve(QEasingCurve::Linear);
    iAnimation->start();

    iMenuHiding = true;
    connect(iAnimation,SIGNAL(finished()), this, SLOT(ContextMenuHidden()));
}

void WPGraphicsList::setFocus(bool moveToCurrentItem)
{
    WPGraphicsListItem * listItem = CurrentListItem();
    if (listItem)
    {
        listItem->setFocus();
        listItem->MarkBogusFocus(false); // gained real focus, disable bogus
    }
    else if (iCurrentItem)
    {
        Utils::ForceFocus(iCurrentItem);
    }
    else if (iLayout->count())
    {
        // Focus first focusable item
        iCurrentItem = iLayout->itemAt(0);
        /// XXX fix iterator so we don't need this
        ++iCurrentItem;
        --iCurrentItem;
        Utils::ForceFocus(iCurrentItem);
    }
    
    if (moveToCurrentItem && iCurrentItem)
    {
        iPannableView->moveToItem(iCurrentItem);
    }
    
    QGraphicsWidget::setFocus();
}

void WPGraphicsList::resizeEvent(QGraphicsSceneResizeEvent* event)
{
    QGraphicsWidget::resizeEvent(event);
    
    WPGraphicsListItem * listItem = CurrentListItem();
    if (iPopupMenu && iPopupMenu->isVisible() && listItem)
        iPopupMenu->move(getMenuLocation(listItem->TopRight().toPoint()));
}

QSizeF WPGraphicsList::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
    {
    return iLayout->sizeHint(which, constraint);
    }

QPoint WPGraphicsList::getMenuLocation(QPoint itemPoint)
{
    const int xCorrection = itemPoint.x() + iPopupMenu->sizeHint().width() - iGraphicsView.viewport()->size().width();
    itemPoint += QPoint(-xCorrection, 0);
    return itemPoint;
}

void WPGraphicsList::keyPressEvent ( QKeyEvent * event  )
{
    qDebug("WP::WPGraphicsList::keyPressEvent Key event %d", event->key());
    
    if (event->key() == Qt::Key_Down)
    {
        if(iPannableView->scrollFocusedItem(PannableView::Down))
            {
            event->accept();
            return;
            }
        ++iCurrentItem;
    }
    else if (event->key() == Qt::Key_Up)
    {
        if(iPannableView->scrollFocusedItem(PannableView::Up))
            {
            event->accept();
            return;
            }
        --iCurrentItem;
    }
    else
    {
        QGraphicsWidget::keyPressEvent(event);
        return;
    }

    if(CurrentListItem())
        {
        iPannableView->moveToItem(iCurrentItem);
        setFocus();
        }
    else if(iCurrentItem)
        {
        Utils::ForceFocus(iCurrentItem);
        iPannableView->moveToItem(iCurrentItem);
        }
    
    event->accept();
}

WPGraphicsListItem * WPGraphicsList::CurrentListItem()
{
    if(iCurrentItem) {
        return qgraphicsitem_cast<WPGraphicsListItem *>(iCurrentItem);
    }
    return 0;
}

bool WPGraphicsList::IsListItem(QGraphicsItem * item)
{
    return (qgraphicsitem_cast<WPGraphicsListItem *>(item) != 0);
}

void WPGraphicsList::ShowSubLayout(SubLayout const & layout, QGraphicsLayoutItem * insertAfter)
{
    // check if already added
    if(Utils::indexOfItem(iLayout, layout.label) >= 0) { return; }
    
    // Insert in reverse order to get right order
    int index = insertAfter ? Utils::indexOfItem(iLayout, insertAfter) + 1 : 0;
    iLayout->insertItem(index, layout.bottomShadow);
    iLayout->insertItem(index, layout.layout);
    iLayout->insertItem(index, layout.topShadow);
    iLayout->insertItem(index, layout.label);
    
    layout.label->show();
    layout.topShadow->show();
    layout.bottomShadow->show();
}

void WPGraphicsList::ShowSubLayout(SubLayout const & layout, LayoutItemList const & insertAfterAlternatives)
{
    for(LayoutItemList::const_iterator it = insertAfterAlternatives.begin();
            it != insertAfterAlternatives.end(); ++it) {
        if(Utils::indexOfItem(iLayout, *it) >= 0) {
            ShowSubLayout(layout, *it);
            return;
        }
    }
    ShowSubLayout(layout, 0); // Show in beginning
}

void WPGraphicsList::HideSubLayout(SubLayout const & layout)
{
    iLayout->removeItem(layout.bottomShadow);
    iLayout->removeItem(layout.layout);
    iLayout->removeItem(layout.topShadow);
    iLayout->removeItem(layout.label);
    
    layout.label->hide();
    layout.topShadow->hide();
    layout.bottomShadow->hide();
}

WPGraphicsList::IteratorGuard::IteratorGuard(WPGraphicsList & list, QGraphicsItem * item)
  : list(list)
  , decremented(false)
{
    if(list.iCurrentItem == item) {
        if(!--list.iCurrentItem) {
            list.iCurrentItem  = 0;
        }
        decremented = true;
    }
}

WPGraphicsList::IteratorGuard::~IteratorGuard()
{
    if(list.iCurrentItem && decremented) {
        ++list.iCurrentItem;
    }
    list.setFocus();
}
